package com.yonyou.pmclouds.plan.rmiimpl;

import com.alibaba.dubbo.config.annotation.Service;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoInsertMultiProcessor;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoUpdateProcessor;
import com.yonyou.pmclouds.basecom.bp.template.MultiInsertBpTemplate;
import com.yonyou.pmclouds.basecom.bp.template.SingleUpdateBpTemplate;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.exception.ConcurrentOperationException;
import com.yonyou.pmclouds.basecom.exception.DataModifiedException;
import com.yonyou.pmclouds.basecom.exception.IllegalRequestArgException;
import com.yonyou.pmclouds.basecom.lock.AutoReleaseLockUtil;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.CommonFieldConst;
import com.yonyou.pmclouds.milestone.service.IMilestoneQuery;
import com.yonyou.pmclouds.organization.entity.OrganizationVO;
import com.yonyou.pmclouds.organization.rmiitf.OrganizationQuery;
import com.yonyou.pmclouds.plan.entity.PlanTempletVO;
import com.yonyou.pmclouds.plan.mapper.PlanTempletMapper;
import com.yonyou.pmclouds.plan.rmiitf.PlanTempletService;
import com.yonyou.pmclouds.projecttype.rmiitf.ProjectTypeQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

@Slf4j
@Service(version = ApplicationConsts.APPLICATION_VERSION, interfaceClass = PlanTempletService.class)
public class PlanTempletServiceImpl implements PlanTempletService {
    private static final String LOCK_PREFIX = "plan_";

    private static final String[] UPDATE_ALL_FIELDS = new String[]{CommonFieldConst.SCODE, CommonFieldConst.SNAME,
            PlanTempletVO.MANAGER, PlanTempletVO.RELATED_BILL_TYPE, PlanTempletVO.EXPECT_DURATION,
            PlanTempletVO.TYPE, PlanTempletVO.DEPEND_TEMPLET, PlanTempletVO.CHILD_COUNT,
            PlanTempletVO.PK_MILESTONE, PlanTempletVO.PK_DEPARTMENT};

    private static final int MAX_LEVEL = 20;
    @Autowired
    private PlanTempletMapper mapper;
    @Autowired
    private IMilestoneQuery milestoneQuery;
    @Autowired
    private OrganizationQuery organizationQuery;
    @Autowired
    private ProjectTypeQuery projectTypeQuery;

    @Override
    public PlanTempletVO[] searchPlanTemp(String keyword, String pkTenant) throws BusinessException {
        PlanTempletVO[] templetVOS = mapper.searchPlanTemp(keyword, pkTenant);
        // 设置里程碑名称、单据类型名称、部门、计划类型名称
        translateRefFields(templetVOS);

        return templetVOS;
    }

    @Override
    public PlanTempletVO[] autoSearchPlanTemp(String pkTenant, String pkOrg, String type) throws BusinessException {
        PlanTempletVO[] templetVOS = mapper.autoSearchPlanTemp(pkTenant,pkOrg,type);
        // 设置里程碑名称、单据类型名称、部门、计划类型名称
        translateRefFields(templetVOS);

        return templetVOS;
    }

    @Override
    public PlanTempletVO[] queryPlanByRoot(String rootId) throws BusinessException {
        if (rootId == null) {
            throw new IllegalRequestArgException();
        }

        PlanTempletVO[] result = mapper.queryPlanByRoot(rootId);
        translateRefFields(result);
        return result;
    }

    @Override
    public void deletePlanTemp(String id, String changeFlag) throws BusinessException {
        // lock project
        if (!AutoReleaseLockUtil.addAutoReleaseLock(LOCK_PREFIX + id)) {
            throw new ConcurrentOperationException();
        }
        PlanTempletVO orig = mapper.getById(id);
        if (!changeFlag.equals(orig.getChangeFlag())) {
            throw new DataModifiedException();
        }

        // 查询节点所有子节点
        List<String> children = new ArrayList<>();
        Queue<String> parents = new LinkedList<>();
        parents.add(orig.getPkPlanTemplet());
        children.add(orig.getPkPlanTemplet());
        do {
            String parent = parents.remove();
            String[] pks = mapper.getChildrenPks(parent);
            if (!ArrayUtils.isEmpty(pks)) {
                children.addAll(Arrays.asList(pks));
                Collections.addAll(parents, pks);
            }
        } while (parents.peek() != null);
        String[] childPks = children.toArray(new String[children.size()]);
        // 删除所有子节点
        for (int i = 0; i < children.size(); i += 100) {
            mapper.deletePlanBatch(ArrayUtils.subarray(childPks, i, i + 100));
        }
        // 修改父节点的count
        mapper.deceaseChildCount(orig.getPkParent(), 1);
    }

    private void deleteBatch(String[] deletePkPlan){
        String[] allChildPks = new String[0];
        for(String plan : deletePkPlan){
            // 查询节点所有子节点
            List<String> children = new ArrayList<>();
            Queue<String> parents = new LinkedList<>();
            parents.add(plan);
            children.add(plan);
            do {
                String parent = parents.remove();
                String[] pks = mapper.getChildrenPks(parent);
                if (!ArrayUtils.isEmpty(pks)) {
                    children.addAll(Arrays.asList(pks));
                    Collections.addAll(parents, pks);
                }
            } while (parents.peek() != null);
            String[] childPks = children.toArray(new String[children.size()]);
            allChildPks = ArrayUtils.addAll(allChildPks, childPks);
        }
        // 删除所有子节点
        if(allChildPks.length > 0) mapper.deletePlanBatch(allChildPks);
    }

    @Override
    public void savePlanTemp(PlanTempletVO temp) throws BusinessException {

        //删除计划
        if(temp.getDeletePlan() != null && temp.getDeletePlan().length > 0){
            deleteBatch(temp.getDeletePlan());
        }

        List<PlanTempletVO> insertList = new ArrayList<>();
        List<PlanTempletVO> updateList = new ArrayList<>();
        Map<String, String> codePlanMap = new HashMap<>();

        genplan(null, temp, insertList, updateList, codePlanMap);//递归遍历计划树
        buildDependPlan(insertList, updateList, codePlanMap);//映射前置计划
        if(!insertList.isEmpty()){
            MultiInsertBpTemplate<PlanTempletVO> bp = new MultiInsertBpTemplate<PlanTempletVO>(mapper, false);
            bp.addBeforeProcessor(new AuditInfoInsertMultiProcessor<PlanTempletVO>());
            bp.insert(insertList.toArray(new PlanTempletVO[0]));
        }
        if(!updateList.isEmpty()){
            SingleUpdateBpTemplate<PlanTempletVO> bp = new SingleUpdateBpTemplate<PlanTempletVO>(mapper, UPDATE_ALL_FIELDS);
            bp.addBeforeProcessor(new AuditInfoUpdateProcessor<PlanTempletVO>());
            for(PlanTempletVO vo : updateList){
                bp.update(vo);
            }
        }
    }

    private void genplan(PlanTempletVO parent, PlanTempletVO temp, List<PlanTempletVO> insertList, List<PlanTempletVO> updateList, Map<String, String> codePlanMap) throws BusinessException {
        //非根计划校验必输项
        if(parent != null) checkPlan(temp);
        //确定是否为新增计划
        boolean isNewTree = true;
        if(temp.getPkPlanTemplet() != null){
            isNewTree = false;
        }else{
            String id = UUID.randomUUID().toString();
            temp.setPkPlanTemplet(id);
        }
        //配置计划编码和主键的映射
        codePlanMap.put(temp.getScode(), temp.getPkPlanTemplet());
        //层级设置pk_parent和pk_root
        if(isNewTree){
            if(parent == null) {
                temp.setPkRoot(temp.getPkPlanTemplet());
            }else{
                temp.setPkRoot(parent.getPkRoot());
                temp.setPkParent(parent.getPkPlanTemplet());
                temp.setPkOrg(parent.getPkOrg());
            }
        }

        //设置租户
        temp.setPkTenant(RuntimeEnvironment.getTenantId());
        //设置来源
        temp.setSource("0");
        //设置类型
        if(parent != null) temp.setType(parent.getType());

        if(temp.getChildren()!=null && temp.getChildren().length > 0){
            temp.setChildCount(temp.getChildren().length);
            for(PlanTempletVO vo : temp.getChildren()){
                genplan(temp, vo, insertList, updateList, codePlanMap);
            }
        }else{
            //校验末级节点层级
            planLevelCheck(temp);
            temp.setChildCount(0);
        }
        if(isNewTree){
            insertList.add(temp);
        } else{
            updateList.add(temp);
        }
    }

    private void checkPlan(PlanTempletVO temp) throws BusinessException {
        if(StringUtils.isBlank(temp.getSname())){
            throw new BusinessException("模板名称不能为空");
        } else if(temp.getSname().length() > 50){
            throw new BusinessException("模板名称长度不能超过50字");
        }
    }

    private void planLevelCheck(PlanTempletVO temp) throws BusinessException {
        String code = temp.getScode();
        int level = 1;
        for (int i = 0; i < code.length(); i++) {
            if (code.charAt(i) == '.') {
                level++;
            }
        }
        if (level > MAX_LEVEL) {
            throw new BusinessException("模板超过最大层级限制！");
        }
    }

    private void buildDependPlan(List<PlanTempletVO> insertList, List<PlanTempletVO> updateList, Map<String, String> codePlanMap) {
        for (PlanTempletVO vo : insertList) {
            if (StringUtils.isNotBlank(vo.getDependPlanName()) && codePlanMap != null) {
                vo.setDependTemplet(codePlanMap.get(vo.getDependPlanName()));
            }
        }
        for (PlanTempletVO vo : updateList) {
            if (StringUtils.isNotBlank(vo.getDependPlanName()) && codePlanMap != null) {
                vo.setDependTemplet(codePlanMap.get(vo.getDependPlanName()));
            } else{
                vo.setDependTemplet("");
            }
        }
    }

    /**
     * 翻译参照字段，设置里程碑名称、关联单据类型名称、nc部门名称
     *
     * @param templetVOS
     */
    private void translateRefFields(PlanTempletVO[] templetVOS) throws BusinessException {
        Set<String> pkMilestoneSet = new HashSet<>();
        Set<String> pkOrgSet = new HashSet<>();
        Set<String> typeSet = new HashSet<>();
        for (PlanTempletVO vo : templetVOS) {
            if (StringUtils.isNotEmpty(vo.getPkMilestone()))
                pkMilestoneSet.add(vo.getPkMilestone());

            if (StringUtils.isNotEmpty(vo.getPkOrg()))
                pkOrgSet.add(vo.getPkOrg());

            if (StringUtils.isNotEmpty(vo.getType()))
                typeSet.add(vo.getType());
        }

        // 查询里程碑名称
        Map<String, String> milestoneMap = null;
        if (pkMilestoneSet.size() > 0) {
            milestoneMap = milestoneQuery.queryNameById(pkMilestoneSet.toArray(new String[0]));
        }

        // 查询组织名称
        Map<String, OrganizationVO> organizationNameMap = queryOrganizationName(pkOrgSet);

        // 查询计划类型名称
        Map<String, String> typeNameMap = null;
        if (typeSet.size() > 0) {
            typeNameMap = projectTypeQuery.queryNamesByIds(typeSet.toArray(new String[0]));
        }

        // 设置参照名称
        for (PlanTempletVO vo : templetVOS) {
            if (milestoneMap != null && StringUtils.isNotEmpty(vo.getPkMilestone()))
                vo.setMilestoneName(milestoneMap.get(vo.getPkMilestone()));

            if (StringUtils.isNotEmpty(vo.getPkOrg())) {
                OrganizationVO organizationVO = organizationNameMap.get(vo.getPkOrg());
                vo.setOrgName(organizationVO == null ? null : organizationVO.getSname());
            }

            if (typeNameMap != null && StringUtils.isNotEmpty(vo.getType()))
                vo.setTypeName(typeNameMap.get(vo.getType()));
        }
    }

    /**
     * 查询组织名称
     *
     * @param pkOrgSet
     * @return
     */
    private Map<String, OrganizationVO> queryOrganizationName(Set<String> pkOrgSet) throws BusinessException {
        Map<String, OrganizationVO> result = new HashMap<>();
        if (CollectionUtils.isEmpty(pkOrgSet))
            return result;

        OrganizationVO[] organizationVOs = organizationQuery.queryByIds(pkOrgSet.toArray(new String[0]));
        if (ArrayUtils.isEmpty(organizationVOs))
            return result;

        for (OrganizationVO vo : organizationVOs) {
            result.put(vo.getPkOrganization(), vo);
        }

        return result;
    }

}
